package db

import (
	"fmt"
	"gitee.com/worklz/yunj-blog-go-micro-common/util"
	"gorm.io/gorm"
	"slices"
	"strings"
)

// 批量修改数据配置
type BatchChangeConfig struct {
	db *DB
	// 条件唯一字段（默认为主键）
	WhereUniqueFields []string
	// 批量修改数量（默认为500）
	BatchSize int
}

// 设置当前批量修改数据配置的DB
func (c *BatchChangeConfig) SetDB(db *DB) (err error) {
	c.db = db
	if len(c.WhereUniqueFields) <= 0 {
		c.WhereUniqueFields, err = db.GetTablePrimaryKeys()
		if err != nil {
			return
		}
	}
	if c.BatchSize <= 0 {
		c.BatchSize = 500
	}
	return
}

// 批量修改数据
func (db *DB) BatchChange(items []map[string]interface{}, configs ...*BatchChangeConfig) *DB {
	if db.Error != nil {
		return db
	}
	if len(items) <= 0 {
		return db
	}
	// 配置
	var config *BatchChangeConfig
	if len(configs) > 0 {
		config = configs[0]
	} else {
		config = &BatchChangeConfig{}
	}
	err := config.SetDB(db)
	if err != nil {
		db.setError(err)
		return db
	}
	// 获取条件唯一字段，默认主键
	if len(config.WhereUniqueFields) <= 0 {
		db.setError(fmt.Errorf("请传入条件唯一字段参数，当前模型未配置主键"))
		return db
	}
	allItemFields := []string{}
	for _, data := range items {
		allItemFields = append(allItemFields, util.MapKeys(data)...)
	}
	allItemFields = util.SliceUnique(allItemFields)
	if !util.SliceIn(allItemFields, config.WhereUniqueFields) {
		db.setError(fmt.Errorf("要执行参数的字段中，需包含字段给定的唯一key或主键！"))
		return db
	}
	tableFields, err := db.GetTableFields()
	if err != nil {
		db.setError(err)
		return db
	}
	// 获取要更新的字段
	allItemFields = util.SliceIntersect(util.SliceDifference(allItemFields, config.WhereUniqueFields), tableFields)
	if len(allItemFields) <= 0 {
		return db
	}
	tableName := db.GetTableName()
	err = db.GormDB().Transaction(func(tx *gorm.DB) (err error) {
		for i := 0; i < len(items); i += config.BatchSize {
			end := i + config.BatchSize
			if end > len(items) {
				end = len(items)
			}
			chunkItems := items[i:end]
			bindings := []interface{}{}
			setSql := ""
			for _, field := range allItemFields {
				fieldSetSql := ""
				for _, item := range chunkItems {
					itemFields := util.MapKeys(item)
					if !slices.Contains(itemFields, field) || !util.SliceIn(itemFields, config.WhereUniqueFields) {
						continue
					}
					fieldSetSql += "WHEN "
					ui := 0
					for _, uniqueField := range config.WhereUniqueFields {
						if ui != 0 {
							fieldSetSql += "AND "
						}
						fieldSetSql += fmt.Sprintf("`%s` = ? ", uniqueField)
						bindings = append(bindings, item[uniqueField])
						ui++
					}
					fieldSetSql += "THEN ? "
					bindings = append(bindings, item[field])
				}
				if fieldSetSql == "" {
					continue
				}
				fieldSetSql = fmt.Sprintf("`%s` = CASE %s ELSE `%s` END ", field, fieldSetSql, field)
				if setSql != "" {
					setSql += ","
				}
				setSql += fieldSetSql
			}
			if setSql == "" {
				continue
			}
			whereSql := ""
			wi := 0
			for _, uniqueField := range config.WhereUniqueFields {
				uniqueFieldVals := util.SliceUnique(util.MapSliceColumn(chunkItems, uniqueField))
				uniqueFieldInSql := strings.TrimSuffix(strings.Repeat("?,", len(uniqueFieldVals)), ",")
				if wi != 0 {
					whereSql += "AND "
				}
				whereSql += fmt.Sprintf("`%s` IN (%s)", uniqueField, uniqueFieldInSql)
				bindings = append(bindings, uniqueFieldVals...)
				wi++
			}

			sql := fmt.Sprintf("UPDATE %s SET %s WHERE %s", tableName, setSql, whereSql)
			// fmt.Printf("数据批量修改：\r\nsql：%v\r\nbindings：%v\r\n", sql, bindings)
			result := tx.Exec(sql, bindings...)
			if result.Error != nil {
				return result.Error
			}
			db.RowsAffected += result.RowsAffected
		}
		return
	})
	if err != nil {
		db.setError(fmt.Errorf("批量修改数据异常！%v", err))
	}

	return db
}
